﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Collections;
using System.Windows.Forms;

using Google.GData.Client;
using Google.GData.Extensions;
using Google.GData.Spreadsheets;

namespace DataSources.Holder
{
    public class GoogleCells : DataSources.GoogleCells, IDataHolder
    {
        public static Hashtable internallyUpdatedCells = new Hashtable(); // HACK

        public event EventHandler EntryChanged;

        public GoogleCells(SpreadsheetsService service, DataSources.GoogleWorksheets owner, string ownerCurrentEntryHash) : 
            base(service, owner, ownerCurrentEntryHash)
        {
        }

        public IDataHolder GetChildDataSource(string entryHash)
        {
            return null;
        }

        public Size GetSize()
        {
            return this.tableSize;
        }

        public Hashtable GetEntries()
        {
            return this.cells;
        }

        private void EntryUpdateValue(string entryHash, string inputText)
        {
            CellEntry cacheEntry = cellEntries[entryHash] as CellEntry;
            if (cacheEntry.Cell.Value != inputText)
            {
                cacheEntry.Cell.InputValue = inputText;
                try
                {
                    cacheEntry.Update();
                }
                catch (Exception ex)
                {
                    EntryResolveConflict(entryHash, inputText, ex);
                }
            }
        }

        private void EntryResolveConflict(string entryHash, string inputText, Exception ex)
        {
            // TODO: implement conflict resolvings
            MessageBox.Show(
                "Can not submit cell " + entryHash + ":\n\r" + ex.ToString(),
                "Conflict. Refresh code table."
            );
        }

        private void EntryInsert(string entryHash, string inputText)
        {
            Point cellCoordinates = Utils.Hash2GridCoordinate(entryHash);
            if (cellCoordinates.Y >= tableSize.Height)
            {
                EntryInsertPrepareRows((uint)cellCoordinates.Y);
            }

            CellEntry newEntry = new CellEntry(
                (uint)cellCoordinates.Y,
                (uint)cellCoordinates.X,
                inputText
            );
            CellEntry insertedEntry = service.Insert(CurrentWorksheet.cellsFeedUri, newEntry);
            cells.Add(entryHash, insertedEntry.Cell.Value);
            cellEntries.Add(entryHash, insertedEntry);
        }

        private void EntryInsertPrepareRows(uint cellCoordinateY)
        {
            (owner as GoogleWorksheets).AddRowsForCell(
                this.CurrentWorksheet.entry.Id.AbsoluteUri, 
                cellCoordinateY
            );
            tableSize = new Size(
                (int)this.CurrentWorksheet.entry.Cols + 1, 
                (int)this.CurrentWorksheet.entry.Rows + 1
            );
        }

        public void Submit(string entryHash, object inputData)
        {
            string inputText = inputData as string;
            System.Diagnostics.Trace.Assert(inputText != null);
            if (inputText == null)
                return;

            if (cellEntries.ContainsKey(entryHash))
            {
                EntryUpdateValue(entryHash, inputText);
            }
            else if (inputText != string.Empty)
            {
                EntryInsert(entryHash, inputText);
            }

            RefreshInternallyUpdatedCells(entryHash, inputText);
        }

        private void RefreshInternallyUpdatedCells(string entryHash, string newCacheValue)
        {
            if (internallyUpdatedCells.ContainsKey(entryHash))
            {
                internallyUpdatedCells[entryHash] = newCacheValue;
            }
            else
            {
                internallyUpdatedCells.Add(entryHash, newCacheValue);
            }
        }

        private CellFeed RequestCellFeed(string blockHash)
        {
            Point cellCoordinate = Utils.Hash2GridCoordinate(blockHash);
            CellQuery cellQuery = new CellQuery(CurrentWorksheet.cellsFeedUri);
            cellQuery.MinimumColumn = (uint)cellCoordinate.X;
            cellQuery.MaximumColumn = (uint)cellCoordinate.X;
            cellQuery.MinimumRow = (uint)cellCoordinate.Y;
            cellQuery.MaximumRow = (uint)cellCoordinate.Y;
            return service.Query(cellQuery) as CellFeed;
        }

        private string RequestText(string entryHash)
        {
            if (cells.ContainsKey(entryHash))
            {
                cells.Remove(entryHash);
                cellEntries.Remove(entryHash);
            }
            CellFeed cellFeed = RequestCellFeed(entryHash);
            if (cellFeed != null && cellFeed.Entries.Count > 0)
            {
                CellEntry cellEntry = cellFeed.Entries[0] as CellEntry;
                cells.Add(entryHash, cellEntry.Cell.Value);
                cellEntries.Add(entryHash, cellEntry);
                return cellEntry.Cell.Value;
            }
            return string.Empty;
        }

        public void Request(string entryHash)
        {
            string blockText = RequestText(entryHash);
            BlockRequestEventArgs re = new BlockRequestEventArgs(entryHash, blockText);
            OnEntryChanged(re);
        }

        protected virtual void OnEntryChanged(EventArgs e)
        {
            EventHandler handler = this.EntryChanged;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        /*
        private string InternalReloadBlock(string blockHash)
        {
            Point cellCoordinate = Utils.Hash2GridCoordinate(blockHash);
            string worksheetFeedUri = GetCurrentAspect().cellsFeedUri;
            CellQuery cellQuery = new CellQuery(worksheetFeedUri);
            cellQuery.MinimumColumn = (uint)cellCoordinate.X;
            cellQuery.MaximumColumn = (uint)cellCoordinate.X;
            cellQuery.MinimumRow = (uint)cellCoordinate.Y;
            cellQuery.MaximumRow = (uint)cellCoordinate.Y;

            CellFeed cellFeed = service.Query(cellQuery) as CellFeed;
            if (cells.ContainsKey(blockHash))
            {
                cells.Remove(blockHash);
                cellEntries.Remove(blockHash);
            }
            if (cellFeed != null && cellFeed.Entries.Count > 0)
            {
                CellEntry cellEntry = cellFeed.Entries[0] as CellEntry;
                string text = cellEntry.Cell.Value as string;
                cells.Add(blockHash, text);
                cellEntries.Add(blockHash, cellEntry);
                return text;
            }
            return string.Empty;
        }

        public void ReloadBlock(string blockHash)
        {
            // TODO: test updated time - actual and cached
            string blockText = InternalReloadBlock(blockHash);
            BlockReloadEventArgs bre = new BlockReloadEventArgs(blockHash, blockText);
            OnBlockChanged(bre);
        }

        public void SubmitBlock(string hash, string inputEntryText)
        {
            // TODO: reload cell before submit

            if (cells.ContainsKey(hash))
            {
                string cachedEntryText = cells[hash] as string;
                if (cachedEntryText != inputEntryText)
                {
                    CellEntry source = cellEntries[hash] as CellEntry;
                    source.Cell.InputValue = inputEntryText;

                    try
                    {
                        source.Update();
                    }
                    catch (Exception ex)
                    {
                        Google.GData.Client.GDataRequestException googleEx =
                            ex as Google.GData.Client.GDataRequestException;
                        if (googleEx != null)
                        {
                            DialogResult desision = MessageBox.Show(
                                "Cell was updated by other user. " +
                                "Press [Retry] to rewrite his changes or " +
                                "[Cancel] to abort your changes and update to latest version.",
                                "Data Source Warning",
                                MessageBoxButtons.RetryCancel
                            );
                            if (desision == DialogResult.Cancel)
                            {
                                ReloadBlock(hash);
                            }
                            else if (desision == DialogResult.Retry)
                            {
                                InternalReloadBlock(hash);
                                if (cells.ContainsKey(hash))
                                {
                                    source = cellEntries[hash] as CellEntry;
                                    source.Cell.InputValue = inputEntryText;
                                    try
                                    {
                                        source.Update();
                                    }
                                    catch (Exception e)
                                    {
                                        MessageBox.Show(
                                            "Failed to rewrite changes. This cell is corrupted. " +
                                            "Please, reload worksheet.\n" + e.ToString(),
                                            "Data Source Error"
                                        );
                                    }
                                }
                                else
                                {
                                    Point cellCoordinates = Utils.Hash2GridCoordinate(hash);
                                    CellEntry newEntry = new CellEntry(
                                        (uint)cellCoordinates.Y,
                                        (uint)cellCoordinates.X,
                                        inputEntryText
                                    );
                                    string worksheetFeedUri = GetCurrentAspect().cellsFeedUri;
                                    service.Insert(worksheetFeedUri, newEntry);

                                    InternalReloadBlock(hash);
                                }
                            }
                        }
                        else
                        {
                            MessageBox.Show(
                                "Failed to submit changes. This cell is corrupted. Please, reload worksheet.",
                                "Data Source Error"
                            );
                        }
                    }
                }
            }
            else
            {
                Point cellCoordinates = Utils.Hash2GridCoordinate(hash);
                if (cellCoordinates.Y >= tableSize.Height)
                {
                    // insert blank rows first
                    string spreadsheetFeedUri = GetCurrentProject().feedUri;
                    WorksheetQuery queryWorksheet = new WorksheetQuery(spreadsheetFeedUri);
                    WorksheetFeed feedWorksheet = service.Query(queryWorksheet);

                    // TODO: figure out more buitiful method to find entry with current id
                    WorksheetEntry worksheetEntry = null;
                    foreach (AtomEntry entry in feedWorksheet.Entries)
                    {
                        if (entry.Id.Equals(currentWorksheetId))
                        {
                            worksheetEntry = entry as WorksheetEntry;
                            break;
                        }
                    }

                    worksheetEntry.Rows = (uint)(cellCoordinates.Y);
                    worksheetEntry.Update();
                    tableSize = new Size((int)worksheetEntry.Cols + 1, (int)worksheetEntry.Rows + 1);
                }

                CellEntry newEntry = new CellEntry(
                    (uint)cellCoordinates.Y,
                    (uint)cellCoordinates.X,
                    inputEntryText
                );
                string worksheetFeedUri = GetCurrentAspect().cellsFeedUri;
                service.Insert(worksheetFeedUri, newEntry);

                ReloadBlock(hash);
            }
        }
        */
    }
}
